package state

import "fmt"

func (this *luaState) Call(nArgs, nResults int) {
	f := this.stack.get(-(nArgs + 1))
	if c, ok := f.(*closure); ok {
		if c.proto != nil {
			this.callLuaClosure(nArgs, nResults, c)
		} else {
			this.callGoClosure(nArgs, nResults, c)
		}
	} else {
		panic("Call(nArgs, nResults int) not  function")
	}
}

/**
nArgs 实际传入的参数数量
nResults 实际需要的结果数量
*/
func (this *luaState) callLuaClosure(nArgs int, nResults int, c *closure) {
	nRegs := int(c.proto.MaxStackSize)
	funcAndArgs := this.stack.PopN(nArgs + 1)
	newStack := NewLuaStack(nRegs+20, this)
	newStack.closure = c
	newStack.PushN(funcAndArgs[1:], int(c.proto.NumParams)) //参数填充到新栈
	newStack.top = nRegs

	if nArgs > int(c.proto.NumParams) && c.proto.IsVarArg == 1 {
		newStack.varargs = funcAndArgs[(int(c.proto.NumParams) + 1):]
	}

	//函数跳转逻辑
	this.PushLuaStack(newStack)
	//exec-inst
	for {
		inst := Instruction(this.Fetch())
		this.PrintStack()
		printInstruction(inst, this.stack.closure.proto, this, this.PC()-1)
		inst.Execute(this)
		if inst.Opcode() == OP_RETURN {
			fmt.Printf("碰到 return 指令   ")
			this.PrintStack()
			break
		}
	}
	this.PopLuaStack()
	//把新调用栈的返回值 放到老调用栈
	if nResults != 0 {
		results := newStack.PopN(newStack.top - nRegs)
		this.stack.PushN(results, nResults)
	}
}

func (this *luaState) callGoClosure(nArgs int, nResults int, c *closure) {
	newStack := NewLuaStack(nArgs+20, this)
	args := this.stack.PopN(nArgs)
	this.stack.pop()
	newStack.PushN(args, nArgs)
	this.PushLuaStack(newStack)
	r := c.goFunc(this)
	this.PopLuaStack()
	if nResults != 0 {
		res := newStack.PopN(r)
		this.stack.PushN(res, nResults)
	}
}
